package cn.iocoder.yudao.demo.redis.controller.admin;

import lombok.RequiredArgsConstructor;
import org.redisson.api.*;
import org.redisson.codec.JsonJacksonCodec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

@Primary
@RequiredArgsConstructor
@RestController
@RequestMapping("/redisson")
public class RedissonController {
    @Autowired
    private RedissonClient redissonClient;

    @GetMapping
    public String redissonTest() {
        JsonJacksonCodec jsonJacksonCodec = new JsonJacksonCodec();

        // *** string
        RBucket<Object> kstring = redissonClient.getBucket("kstring", jsonJacksonCodec);
        kstring.set("1111");

        // *** list
        RList<Object> klist = redissonClient.getList("klist", jsonJacksonCodec);
        for (int i = 0; i < 10; i++) {
            klist.add("瓜田李下" + i);
        }


        // *** set
        RSet<Object> kset = redissonClient.getSet("kset", jsonJacksonCodec);
        for (int i = 0; i < 10; i++) {
            kset.add("瓜田李下" + i);
        }

        // *** zset
        RScoredSortedSet<Object> kSortedSet = redissonClient.getScoredSortedSet("kzset", jsonJacksonCodec);
        for (int i = 0; i < 10; i++) {
            kSortedSet.add(i, "瓜田李下" + i);
        }

        // *** map
        RMap<Object, Object> kmap = redissonClient.getMap("kmap", jsonJacksonCodec);
        for (int i = 0; i < 10; i++) {
            kmap.put(i, "瓜田李下" + i);
        }

        // *** 使用Redis事务

        // Redis事务的确有一些限制和缺陷： 事务特性（原子性，一致性，隔离性，持久性）
        //不支持回滚机制：Redis 事务虽然提供了事务的功能，但是不支持事务的回滚（ROLLBACK）操作。如果在事务执行过程中发生了错误，只能通过 DISCARD 命令取消事务，无法回滚到之前的状态。
        //不支持原子性操作之外的命令：Redis 事务仅对支持原子性操作的命令（例如 SET、GET、DEL 等）起作用。对于像 WATCH、MULTI、EXEC、DISCARD 这样的事务控制命令本身，不是原子性的。
        //不是严格的事务隔离级别：Redis 的事务隔离性不像关系型数据库那样严格。如果多个事务同时修改同一键的值，后提交的事务会覆盖先提交的事务，这可能导致数据不一致的问题。
        //执行事务期间阻塞其他客户端：在执行事务期间，Redis 会阻塞其他客户端的请求，直到当前事务执行完成。如果事务执行时间过长，会导致其他客户端的请求排队等待，降低了系统的并发性能。
        //事务性能较低：Redis 的事务是单线程执行的，事务中的命令是顺序执行的，这限制了事务的并发性能。如果事务中包含大量的命令或者复杂的计算，可能会导致事务执行时间过长，影响系统的性能表现。

        // 设置事务选项
        TransactionOptions options = TransactionOptions.defaults()
                .timeout(5, TimeUnit.SECONDS) // 设置事务超时时间为100秒
                //.syncSlaves(1, 100, TimeUnit.SECONDS) // 同步到2个从节点
                ;
        // 开始事务
        RTransaction transaction = redissonClient.createTransaction(options);
        try {
            // 在事务中执行一些操作
            // 这里是伪代码，你可以添加你的实际 Redisson 操作
            transaction.getBucket("ktx1", jsonJacksonCodec).set("vtx1");
            transaction.getBucket("ktx2", jsonJacksonCodec).set("vtx2");
            // 提交事务
            transaction.commit();
        } catch (Exception e) {
            // 如果出现异常，回滚事务
            transaction.rollback();
        }


        // *** 使用Lua脚本
        StringJoiner luaScript = new StringJoiner(" ");
        luaScript //
                .add("redis.call('SET', KEYS[1], ARGV[1]);")
                .add("redis.call('SET', KEYS[2], ARGV[2]);")
        ;
        // Lua脚本字符串
        List<Object> keys = Arrays.asList("klua1", "klua2"); // 脚本中使用的键列表
        // 获取脚本执行器
        RScript script = redissonClient.getScript(jsonJacksonCodec);


        // 同步执行脚本
        Object object = script.eval(RScript.Mode.READ_WRITE, luaScript.toString(), RScript.ReturnType.VALUE, keys, "vlua1", "vlua2");
        System.out.println("Script execution result: " + object);

        // 异步执行脚本
        RFuture<Object> objectRFuture = script.evalAsync(RScript.Mode.READ_WRITE, luaScript.toString(), RScript.ReturnType.VALUE, keys, "vlua1", "vlua2");
        try {
            Object obj = objectRFuture.get();
            System.out.println("Script execution result: " + obj);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        return "ok";
    }
}
